<?
/*-- Project Introduce --*/


/**
 * 事件管理器
 *
 * @author		alee
 * @access		public
 */
class JCAT_EventManager
{
	
	const PHP_TICK = 1 ;
	const PHP_AUTOLOAD = 2 ;
	const PHP_UNCATCH_EXCEPTION = 4 ;
	const PHP_SHUTDOWN = 8 ;
	const PHP_ERROR = 16 ;
	const PHP_ALL = 31 ;		// all but PHP_TICK
	
	const NS_KERNEL_HANDLE = 'NS_KERNEL_HANDLE' ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @static
	 * @var		array
	 */
	static private $arrKernelEvents = array (
			self::PHP_AUTOLOAD
			, self::PHP_TICK
			, self::PHP_UNCATCH_EXCEPTION
			, self::PHP_SHUTDOWN
			, self::PHP_ERROR
	);
	
	const EXCEPID_STOP_EVENT = 0 ;
	const EXCEPID_RETURN_HANDLE = 1 ; 
	
	/**
	 * Description
	 *
	 * @access	public
	 * @static 
	 * @return	JCAT_EventManager
	 */
	static public function GetGlobalInstance()
	{
		if( !self::$aGlobalInstance )
		{
			self::$aGlobalInstance = new JCAT_EventManager() ;
		}
		
		return self::$aGlobalInstance ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param 	$aInstance	JCAT_EventManager
	 * @static 
	 * @return	void
	 */
	static public function SetGlobalInstance(JCAT_EventManager $aInstance)
	{
		self::$aGlobalInstance = $aInstance ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$bGlobal=false
	 * @return	void
	 */
	public function JCAT_EventManager($bGlobal=false)
	{
		if($bGlobal)
		{
			self::SetGlobalInstance($this) ;
		}
	}
		
	/**
	 * 声明一个事件
	 *
	 * @access	public
	 * @param 	$sEventName								string				事件名称
	 * @param 	$sNamespace='\\'						string				命名空间
	 * @param 	$arrDescription=array('','JCAT')		array				参数
	 * @param 	$arrArgvsList=array()					array				参数表
	 * @return	void
	 */
	public function DefineEvent( $sEventName, $sNamespace='\\', $arrDescription=array('','JCAT'), $arrArgvsList = array() )
	{
		$this->arrHandles[$sNamespace][$sEventName] = array() ;
		$this->arrEvents[$sNamespace][$sEventName] = array(
					'Description' => $arrDescription
					, 'ArgvList' => $arrArgvsList
		) ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param 	$sEventName								string				事件名称
	 * @param 	$sNamespace='\\'						string				命名空间
	 * @return	bool
	 */
	public function HasEventDefined($sEventName, $sNamespace='\\')
	{
		return isset($this->arrEvents[$sNamespace][$sEventName]) ;
	}
	
	/**
	 * 注册一个事件处理回调函数，返回 handle key
	 *
	 * @access	public
	 * @param 	$sEventName				string			事件名称
	 * @param 	$Callback				callback		回调函数
	 * @param 	$arrArgvs=array()		array			参数
	 * @param 	$sNamespace='\\'		string			命名空间
	 * @return	string
	 */
	public function RegisterHandle( $sEventName, $Callback, $arrArgvs = array(), $sNamespace='\\' )
	{
		if( !is_callable($Callback) )
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'正在将一个无效的事件相应函数(%s to %s::%s)注册到事件管理器'
				, 'JCAT', null
				, $Callback, $sNamespace, $sEventName 
			)) ;
		}
		
		$this->arrHandles[$sNamespace][$sEventName]
			[($sHandleKey=++self::$sHandleKeyAssigned)] = array($Callback,$arrArgvs) ;
		
		return $sHandleKey ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param 	$sEventName				string			事件名称
	 * @param 	$Callback				callback		回调函数
	 * @param 	$sNamespace='\\'		string			命名空间
	 * @return	callback
	 */
	public function UnregisterHandle($sEventName,$sHandleKey,$sNamespace='\\')
	{
		$Handle = isset($this->arrHandles[$sNamespace][$sEventName][$sHandleKey])?
			$this->arrHandles[$sNamespace][$sEventName][$sHandleKey]: null ;
		
		unset($this->arrHandles[$sNamespace][$sEventName][$sHandleKey]) ;
		
		return $Handle ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param 	$sEventName				string			事件名称
	 * @param 	$Callback				callback		回调函数
	 * @param 	$sNamespace='\\'		string			命名空间
	 * @return	void
	 */
	public function ClearHandles($sEventName,$sNamespace='\\')
	{
		unset($this->arrHandles[$sNamespace][$sEventName]) ;
	}

	/**
	 * 查找一个事件处理函数
	 *
	 * @access	private
	 * @param	$Callback
	 * @param	$sHandle
	 * @return	string
	 */
	public function FindHandle($sEventName,$Handle,$sNamespace='\\')
	{
		if( isset($this->arrHandles[$sNamespace][$sEventName]) )
		{
			foreach ($this->arrHandles[$sNamespace][$sEventName] as $sHandleKey=>$arrHandleItem)
			{
				if( JCAT_Global::IsSameCallback($Handle,$arrHandleItem[0]) )
				{
					return $sHandleKey ;
				}
			}
		}
		return null ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param 	$sEventName				string			事件名称
	 * @param 	$Callback				callback		回调函数
	 * @param 	$arrArgvs=array()		array			参数
	 * @return	string
	 */
	public function RegisterKernelHandle($nEvent,$Callback, $arrArgvs = array())
	{
		return $this->RegisterHandle($nEvent,$Callback,$arrArgvs,self::NS_KERNEL_HANDLE) ;
	}
	
	/**
	 * 接管php内核事件
	 *
	 * @access	public
	 * @param	$nEvents=self::PHP_ALL
	 * @param	$bDisconnectFirst=false
	 * @return	void
	 */
	public function TakeoverKernelEvent($nEvents=self::PHP_ALL)
	{
		$this->nTakeoverKernelEvents|= $nEvents ;
		
		// auto
		if($nEvents&self::PHP_AUTOLOAD)
		{
			spl_autoload_register(array($this,'OnAutoload')) ;
		}
		
		// uncatch exception
		if($nEvents&self::PHP_UNCATCH_EXCEPTION)
		{
			set_exception_handler(array($this,'OnUncatchException')) ;
		}
		
		// shutdown
		if($nEvents&self::PHP_SHUTDOWN)
		{
			register_shutdown_function(array($this,'OnShutdown')) ;
		}
		
		// error
		if($nEvents&self::PHP_ERROR)
		{
			set_error_handler(array($this,'OnError')) ;
		}
		
		// tick (tick在php5.3中为'Deprecated'，会触发error,必须在error的后面)
		if($nEvents&self::PHP_TICK)
		{
			register_tick_function(array($this,'OnTick')) ;
		}
	}
	
	/**
	 * 释放对PHP内核事件的接管
	 *
	 * @access	public
	 * @param	$nEvents
	 * @return	void
	 */
	public function ReleaseKernelEvent($nEvents=self::PHP_ALL,$bClearHandles=false)
	{
		$this->nTakeoverKernelEvents^= ($this->nTakeoverKernelEvents&$nEvents) ;
		
		if($bClearHandles)
		{
			foreach (self::$arrKernelEvents as $nEvent)
			{
				$this->ClearHandles($nEvent,self::NS_KERNEL_HANDLE) ;
			}
		}
	}
	
	/**
	 * 响应事件：未捕捉的异常
	 *
	 * @access	public
	 * @return	void
	 */
	public function OnUncatchException($aException)
	{
		if($this->nTakeoverKernelEvents&self::PHP_UNCATCH_EXCEPTION)
		{
			$this->Emit(self::PHP_UNCATCH_EXCEPTION,array($aException),self::NS_KERNEL_HANDLE) ;
		}
	}

	/**
	 * 响应 PHP错误事件 的方法
	 *
	 * @access	public
	 * @return	void
	 */
	public function OnShutdown()
	{
		if($this->nTakeoverKernelEvents&self::PHP_SHUTDOWN)
		{
			$this->Emit(self::PHP_SHUTDOWN,array(),self::NS_KERNEL_HANDLE) ;
		}
	}

	/**
	 * 响应 PHP错误事件 的方法
	 *
	 * @access	public
	 * @return	void
	 */
	public function OnError($nErrorNo,$sErrStr,$sErrFile,$nErrLine)
	{
		// E_DEPRECATED support form PHP5.3RC
		if( defined('E_DEPRECATED') and ($nErrorNo&E_DEPRECATED) )
		{
			// 忽略对 register_tick_function 的 E_DEPRECATED 信息
			$arrRes = array() ;
			if( preg_match('/^(.+)\(\)/',$sErrStr,$arrRes) and $arrRes[1]==='register_tick_function' )
			{
				return ;
			}
		}
		
		// 
		if($this->nTakeoverKernelEvents&self::PHP_ERROR)
		{
			$Ret = $this->Emit(self::PHP_ERROR,array($nErrorNo,$sErrStr,$sErrFile,$nErrLine),self::NS_KERNEL_HANDLE) ;
			return ($Ret===null)? false: $Ret ;
		}
		
		else
		{
			// 执行 php 的内置处理
			return false ;
		}
	}

	/**
	 * 响应 PHP错误事件 的方法
	 *
	 * @access	public
	 * @return	void
	 */
	public function OnTick()
	{
		if($this->nTakeoverKernelEvents&self::PHP_TICK)
		{
			$this->Emit(self::PHP_TICK,array(),self::NS_KERNEL_HANDLE) ;
		}
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$sClassname
	 * @return	void
	 */
	public function OnAutoload($sClassname)
	{
		if($this->nTakeoverKernelEvents&self::PHP_AUTOLOAD)
		{
			$this->Emit(self::PHP_AUTOLOAD,array($sClassname),self::NS_KERNEL_HANDLE) ;
		}
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param 	$sEventName				string			事件名称
	 * @param 	$arrInArgvs=array()		array			参数
	 * @param 	$sNamespace='\\'		string			命名空间
	 * @return	void
	 */
	public function Emit($sEventName,$arrInArgvs = array(),$sNamespace='\\')
	{
		$ReturnValue = null ;
		
		if( !isset($this->arrHandles[$sNamespace][$sEventName]) )
		{
			return $ReturnValue ;
		}
		
		foreach($this->arrHandles[$sNamespace][$sEventName] as $arrHandleEle) 
		{
			list($Handle,$arrArgvs) = $arrHandleEle ;
			$arrArgvs = array_merge($arrInArgvs,$arrArgvs) ;
			
			try
			{
				call_user_func_array($Handle,$arrArgvs) ;
			}
			
			catch (JCAT_EventProcessException $aExcep)
			{
				switch ($aExcep->getCode())
				{
					// stop event
					case self::EXCEPID_STOP_EVENT :
						break(2) ;
					
					// return this handle, and set return value 
					case self::EXCEPID_RETURN_HANDLE :
						list($ReturnValue) = $aExcep->GetArgvs() ;
						break ;
					
					// unknow ??
					default	:
						throw $aExcep ;
						break ;
				}
			}
		}
		
			return $ReturnValue ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @static
	 * @return	void
	 */
	static public function StopEvent()
	{
		throw new JCAT_EventProcessException(self::EXCEPID_STOP_EVENT) ;
	}
	
	
	/**
	 * Description
	 *
	 * @access	public
	 * @static
	 * @return	void
	 */
	static public function ReturnHandle($ReturnValue)
	{
		throw new JCAT_EventProcessException(self::EXCEPID_RETURN_HANDLE,array($ReturnValue)) ;
	}
	
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		array
	 */
	private $arrHandles = array() ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		array
	 */
	private $arrEvents = array() ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		int
	 */
	private $nTakeoverKernelEvents = 0 ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @static
	 * @var		string
	 */
	static private $sHandleKeyAssigned = 0 ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @static
	 * @var		JCAT_EventManager
	 */
	static private $aGlobalInstance ;
}



?>